package com.keyholesoftware.trango.service.jpa;

import java.io.Serializable;
import java.util.List;

import javax.annotation.PostConstruct;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Query;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.orm.jpa.EntityManagerFactoryUtils;
import org.springframework.orm.jpa.SharedEntityManagerCreator;
import org.springframework.transaction.annotation.Transactional;

import com.keyholesoftware.trango.annotations.TrangoService;
import com.keyholesoftware.trango.entity.Entity;
import com.keyholesoftware.trango.service.PersistentService;

public class JPAPersistentService implements PersistentService<Query> {

	@Autowired
	ApplicationContext context;

	private EntityManager entityManager;
	private Class<?> entityClass;

	@Override
	@Transactional(readOnly = true)
	public Long count() {
		return (Long) this.entityManager.createQuery("select count(o) from " + this.entityClass.getSimpleName() + " o").getSingleResult();
	}

	@Override
	public Query createQuery(String jql) {
		return this.entityManager.createQuery(jql);
	}

	@Override
	public <E extends Entity> List<E> find(Query query) {
		// TODO implement Query based
		return null;
	}

	@Override
	@Transactional(readOnly = true)
	@SuppressWarnings("unchecked")
	public <E extends Entity> E find(Serializable identifier, String... eagerPaths) {
		return (E) this.entityManager.find(this.entityClass, identifier);
	}

	@Override
	@Transactional(readOnly = true)
	@SuppressWarnings("unchecked")
	public <E extends Entity> List<E> findAll(String... eagerPaths) {
		String queryString = "from " + this.entityClass.getSimpleName();
		Query queryObject = this.entityManager.createQuery(queryString);
		return queryObject.getResultList();
	}

	@PostConstruct
	public void initialize() {
		TrangoService serviceAnnotation = this.getClass().getAnnotation(TrangoService.class);
		String unitName = serviceAnnotation.persistenceUnit();
		EntityManagerFactory factory = EntityManagerFactoryUtils.findEntityManagerFactory(this.context, unitName);
		this.entityManager = SharedEntityManagerCreator.createSharedEntityManager(factory, null);
		this.entityClass = serviceAnnotation.entity();
	}

	@Override
	@Transactional
	public <E extends Entity> E merge(E entity) {
		E merged = this.entityManager.merge(entity);
		this.entityManager.flush();
		return merged;
	}

	@Override
	@Transactional
	public <E extends Entity> E persist(E entity) {
		this.entityManager.persist(entity);
		return entity;
	}

	@SuppressWarnings("unchecked")
	@Override
	@Transactional
	public <E extends Entity> void remove(Serializable identifier, E entity) {
		if (!this.entityManager.contains(entity)) {
			entity = (E) this.entityManager.find(entity.getClass(), identifier);
		}
		this.entityManager.remove(entity);
	}
}
